/*******************************************************************************
 * This file is part of Crunch Network.
 *
 * Crunch Network is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * Crunch Network is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Crunch Network.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package com.crunch.network;

import java.util.Arrays;

/**
 * TODO
 *
 * should note: if state tables are reused, "reset" should be called
 */
public abstract class StateTable {
	/**
	 * The constructor takes the state table type ID.
	 *
	 * @param   id the state table type ID. This ID should match the ID passed into the {@link MessageRegistry#register
	 *          (int, crunch.network.MessageRegistry.StateTableFactory)} method.
	 */
	protected StateTable(int id) {
		this.id = id;
	}

	/**
	 * Called by subclasses to assign the state table a list of state elements to keep track of. This method should be
	 * called in the constructor of subclasses, after state elements have been initialized.
	 *
	 * @param   stateElements the list of state elements making up the state table which should be tracked.
	 * @throws  IllegalStateException if this method has already been called.
	 */
	protected void setStateElements(StateElement... stateElements) {
		if (this.stateElements != null) {
			throw new IllegalStateException("State table elements already specified");
		}
		// copy the state elements into internal array
		this.stateElements = Arrays.copyOf(stateElements, stateElements.length);
		reset();
	}

	/**
	 * Resets the state of all state elements. This should be called if state table pooling/reuse is implemented.
	 */
	protected void reset() {
		for (StateElement e : stateElements) {
			e.reset();
		}
	}

	/**
	 * Returns {@code true} if any state elements belonging to this table require that an update be sent out. If the
	 * network transmission mode of any state element is {@link StateElement.NetworkTransmissionMode#ALWAYS}, this
	 * method always returns {@code true}.
	 *
	 * @return  whether any state elements require that an update be sent out.
	 */
	public boolean requiresStateUpdate() {
		for (StateElement stateElement : stateElements) {
			if (StateElement.shouldBeSent(false, stateElement.getNetworkTransmissionMode(), stateElement.isDirty())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns the state table type ID.
	 *
	 * @return  the state table type ID.
	 */
	public int getId() {
		return id;
	}

	/**
	 * Called when the state table is first created. State elements can be queried to determine initial values. All
	 * elements are valid for querying at this point.
	 *
	 * @param   ghostId the ID of the ghost which this state table belongs to.
	 */
	public abstract void onCreated(int ghostId);

	/**
	 * Called when values in the state table have been updated. State elements can be queried to determine individual
	 * update status. Note that the values of state elements which have not been updated should not be used in this
	 * method, as they will contain invalid values.
	 *
	 * @param   ghostId the ID of the ghost which this state table belongs to.
	 */
	public abstract void onUpdated(int ghostId);

	/**
	 * Called when the state table has been destroyed. No values should be queried, as they will all be invalid.
	 *
	 * @param   ghostId the ID of the ghost which this state table belongs to.
	 */
	public abstract void onDestroyed(int ghostId);

	/**
	 * Returns the number of elements this state table contains.
	 *
	 * @return  the number of elements this state table contains.
	 */
	int getElementCount() {
		return stateElements.length;
	}

	/**
	 * Returns the {@code i}th element in the state table.
	 *
	 * @param   i the index of the state element to return.
	 * @return  the {@code i}th element in the state table.
	 */
	StateElement getStateElement(int i) {
		return stateElements[i];
	}

	// ID of the state table
	private int id;

	// list of elements in the state table
	private StateElement[] stateElements;
}
